﻿using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System;
using DeviceDebugger.View;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using GalaSoft.MvvmLight.Messaging;
using DeviceDebugger.Model;
using System.Windows;
using DeviceDebugger.DataAccess;
using System.Windows.Media;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Threading;
using System.Collections.ObjectModel;
using DeviceDebugger.Common;
using System.Windows.Threading;
using DeviceDebugger.Model.Enums;

namespace DeviceDebugger.ViewModel
{
    public class RunWindowViewModel : ViewModelBase
    {

        private RunWindowProperty _windowProperty;
        public RunWindowProperty WindowProperty
        {
            get { return _windowProperty; }
            set { _windowProperty = value; RaisePropertyChanged(() => WindowProperty); }
        }

        private StackerModel _model;
        public StackerModel Model
        {
            get { return _model; }
            set { _model = value; RaisePropertyChanged(() => Model); }
        }

        private ObservableCollection<StackerTasksModel> _listStackerTask;
        /// <summary>
        /// 当前堆垛机的自动任务列表
        /// </summary>
        public ObservableCollection<StackerTasksModel> ListStackerTask
        {
            get { return _listStackerTask; }
            set
            {
                _listStackerTask = value;
                if (value != null && value.Count > 0)
                {
                    DicTaskList["Next"] = ListStackerTask.Where(a => !a.IsExecute).OrderBy(a => a.Id).First();
                }
                RaisePropertyChanged(() => ListStackerTask);
            }
        }

        private ObservableDictionary<string, StackerTasksModel> _dicTaskList;
        /// <summary>
        /// 任务列表，只显示上一个，当前，下一个
        /// </summary>
        public ObservableDictionary<string, StackerTasksModel> DicTaskList
        {
            get { return _dicTaskList; }
            set { _dicTaskList = value; RaisePropertyChanged(() => DicTaskList); }
        }

        private ObservableCollection<RunLogs> _debugLogsList;
        /// <summary>
        /// 调试日志
        /// </summary>
        public ObservableCollection<RunLogs> DebugLogsList
        {
            get { return _debugLogsList; }
            set { _debugLogsList = value; RaisePropertyChanged(() => DebugLogsList); }
        }



        private StackerVisitor StackerVisitor;
        private S7Client PLC;
        /// <summary>
        /// 定时器，用于刷新任务执行进度，是否执行完成
        /// </summary>
        private DispatcherTimer timer = new();
        /// <summary>
        /// 定时器，用于连接PLC后读取PLC点值
        /// </summary>
        private DispatcherTimer MonitorTimer = new();


        public RelayCommand<Window> CloseCommand => new(CloseCommand_Click);
        public RelayCommand LinkPLCCommand => new(LinkPLCCommand_Click);
        public RelayCommand StorageDisableCommand => new(StorageDisableCommand_Click);
        public RelayCommand CreateTaskCommand => new(CreateTaskCommand_Click);
        public RelayCommand AutoRunCommand => new(AutoRunCommand_Click);
        public RelayCommand HalfRunCommand => new(HalfRunCommand_Click);
        public RelayCommand HandRunCommand => new(HandRunCommand_Click);
        public RelayCommand MonitorProtocolCommand => new RelayCommand(MonitorProtocolCommand_Click);


        public RunWindowViewModel()
        {
            Init_Window();
            //接受堆垛机对象
            Messenger.Default.Register<StackerModel>(this, "Running", (a) =>
            {
                if (a != null) Model = a;
                GetTaskList(a.Id);
            });
        }

        /// <summary>
        /// 获取任务列表
        /// </summary>
        /// <param name="id"></param>
        private void GetTaskList(Int64 id)
        {
            ListStackerTask = new(DataVisitor.GetStackerTasks(id));
            //if (ListStackerTask.Count > 0)
            //    DicTaskList["Next"] = ListStackerTask.Where(a => !a.IsExecute).OrderBy(a => a.Id).First();
        }

        /// <summary>
        /// 初始化页面元素
        /// </summary>
        private void Init_Window()
        {
            //初始化页面中控件显示
            WindowProperty = new();

            PLC = new();
            Model = new();
            StackerVisitor = new(PLC);
            ListStackerTask = new();
            DebugLogsList = new();

            DicTaskList = new ObservableDictionary<string, StackerTasksModel> {
                {"Prev",new StackerTasksModel() },
                {"Current",new StackerTasksModel() },
                {"Next",new StackerTasksModel() }
            };

            timer.Interval = TimeSpan.FromSeconds(3);
            timer.Tick += new EventHandler(TaskProcss);
        }

        /// <summary>
        /// 连接PLC按钮
        /// </summary>
        private void LinkPLCCommand_Click()
        {
            if (Model is null)
                return;
            if (PLC.Connected() == false)
            {
                WindowProperty.tbtnConn = "正在连接";
                if (PLC.ConnectTo(Model.IPAdress, 0, 1) == 0)
                {
                    //连接成功 
                    WindowProperty.tbtnConn = "断开PLC";
                    WindowProperty.txtConnForeground = Brushes.Green;
                    WindowProperty.txtConnText = "已连接";
                    StackerVisitor = new(PLC);
                    Monitor(TimeSpan.FromSeconds(3));
                }
                else
                {
                    //连接失败
                    MessageBox.Show("连接失败，请检查网络！");
                    WindowProperty.tbtnConn = "连接PLC";
                }
            }
            else
            {
                //断开PLC
                PLC.Disconnect();
                Init_Window();
                //断开PLC同时也停止刷新点值的计时器
                MonitorTimer.Stop();
            }
        }

        /// <summary>
        /// 测点监控界面
        /// </summary>
        private void MonitorProtocolCommand_Click()
        {
            MonitorView monitorView = new MonitorView();
            monitorView.Show();
        }

        /// <summary>
        /// 关闭窗口按钮
        /// </summary>
        /// <param name="window"></param>
        private void CloseCommand_Click(Window window)
        {
            window.Closed += Window_Closed;
            if (WindowProperty.isAuto)
            {
                if (MessageBox.Show("关闭窗口的同时会停止自动作业和关闭PLC连接，是否确定关闭本窗口？", "提示", MessageBoxButton.OKCancel, MessageBoxImage.Warning) == MessageBoxResult.OK)
                {
                    window.Close();
                }
            }
            else if (PLC.Connected())
            {
                if (MessageBox.Show("关闭窗口的同时会关闭PLC连接，是否确定关闭本窗口？", "提示", MessageBoxButton.OKCancel, MessageBoxImage.Warning) == MessageBoxResult.OK)
                {
                    window.Close();
                }
            }
            else
                window.Close();
        }

        private void Window_Closed(object sender, EventArgs e)
        {
            if (timer.IsEnabled)
            {
                timer.Stop();
                WindowProperty.isAuto = false;
            }

            if (PLC.Connected())
                PLC.Disconnect();

            if (MonitorTimer.IsEnabled)
                MonitorTimer.Stop();
        }

        /// <summary>
        /// 禁用库位按钮
        /// </summary>
        private void StorageDisableCommand_Click()
        {
            StorageDisableView storageDisableView = new();
            Messenger.Default.Send(Model, "StorageDisable");
            storageDisableView.Show();
        }

        /// <summary>
        /// 生成任务按钮
        /// </summary>
        private void CreateTaskCommand_Click()
        {
            CreateTask();
            GetTaskList(Model.Id);
        }

        /// <summary>
        /// 自动执行按钮
        /// </summary>
        private void AutoRunCommand_Click()
        {
            if (!PLC.Connected() || StackerVisitor == null)
            {
                MessageBox.Show("PLC连接异常！");
                return;
            }

            if (!WindowProperty.isAuto)
            {
                WindowProperty.isAuto = true;
                WindowProperty.txtAutoExecute = "停止执行";
                AutoExecuteTask(TimeSpan.FromSeconds(0));
            }
            else
            {
                WindowProperty.isAuto = false;
                WindowProperty.txtAutoExecute = "自动执行";
            }
        }
        /// <summary>
        /// 半自动执行按钮
        /// </summary>
        private void HalfRunCommand_Click()
        {
            if (!PLC.Connected() || StackerVisitor == null)
            {
                MessageBox.Show("PLC连接异常！");
                return;
            }

            if (WindowProperty.isAuto)
            {
                if (MessageBox.Show("此操作将停止自动执行，是否继续", "提示", MessageBoxButton.OKCancel, MessageBoxImage.Information) == MessageBoxResult.OK)
                {
                    WindowProperty.isAuto = false;
                    WindowProperty.txtAutoExecute = "自动执行";
                    HalfExecuteTask();
                }
            }
            else
            {
                HalfExecuteTask();
            }
        }

        /// <summary>
        /// 手动执行按钮
        /// </summary>
        private void HandRunCommand_Click()
        {
            if (!PLC.Connected() || StackerVisitor == null)
            {
                MessageBox.Show("PLC连接异常！");
                return;
            }
            if (WindowProperty.isExecute)
            {
                MessageBox.Show("堆垛机正在执行作业，请稍后再下发手动作业命令！");
                return;
            }

            if (WindowProperty.isAuto)
            {
                if (MessageBox.Show("此操作将停止自动执行，是否继续", "提示", MessageBoxButton.OKCancel, MessageBoxImage.Information) == MessageBoxResult.OK)
                {
                    WindowProperty.isAuto = false;
                    WindowProperty.txtAutoExecute = "自动执行";
                    HandExecuteTask();
                }
            }
            else
            {
                HandExecuteTask();
            }
        }



        #region 生成作业相关方法
        /// <summary>
        /// 生成自动作业
        /// </summary>
        private void CreateTask()
        {
            if (Model is null)
                return;

            //先清除目前存在的自动任务
            if (DataVisitor.DeleteTask(Model.Id))
            {
                try
                {
                    #region 笛卡尔乘积计算所有货位
                    List<string[]> list = new();
                    string[] RowNumber = new string[Convert.ToInt32(Model.RowCount)];
                    string[] ColumNumber = new string[Convert.ToInt32(Model.ColumCount)];
                    string[] LayerNumber = new string[Convert.ToInt32(Model.LayerCount)];

                    for (int r = 1; r <= Convert.ToInt32(Model.RowCount); r++)
                    {
                        RowNumber[r - 1] = r.ToString().PadLeft(2, '0');
                    }
                    for (int c = 1; c <= Convert.ToInt32(Model.ColumCount); c++)
                    {
                        ColumNumber[c - 1] = c.ToString().PadLeft(2, '0');
                    }
                    for (int l = 1; l <= Convert.ToInt32(Model.LayerCount); l++)
                    {
                        LayerNumber[l - 1] = l.ToString().PadLeft(2, '0');
                    }
                    list.Add(RowNumber);
                    list.Add(ColumNumber);
                    list.Add(LayerNumber);
                    //货位集合
                    List<string> result = new List<string>();
                    Descartes(list, 0, result, string.Empty);
                    #endregion

                    //获取当前堆垛机禁用货位，并从货位集合中移除
                    List<StorageDisableModel> listStorageDisable = DataVisitor.GetStorageDisables(Model.Id);
                    listStorageDisable.ForEach((a) => { result.Remove(a.Location); });

                    //随机两个下标数组，长度为可用货位的个数
                    int[] starIndex;
                    int[] endIndex;
                    GetRandomArray(result.Count, out starIndex, out endIndex);

                    List<StackerTasksModel> listModel = new();
                    for (int i = 0; i < result.Count; i++)
                    {
                        StackerTasksModel stmodel = new();
                        stmodel.StartLocation = result[starIndex[i]];
                        stmodel.EndLocation = result[endIndex[i]];
                        stmodel.StackerId = Model.Id;
                        listModel.Add(stmodel);
                    }
                    List<StackerTasksModel> listTemp = new();
                    SortList(listModel, listTemp, "01-01-01");

                    //分批存储至数据库中，以防一次提交数据量过大
                    int forTime = (int)Math.Ceiling((decimal)listTemp.Count / 100);

                    for (int i = 0; i < forTime; i++)
                    {
                        //从集合中取出前100个添加至数据库
                        var addList = listTemp.Take(100).ToList();
                        DataVisitor.AddStackerTasks(addList);
                        //添加后移除前100个
                        listTemp.RemoveRange(0, addList.Count);
                    }
                    DebugLogsList.Add(new RunLogs(Model.Id, DateTime.Now, Model.Name + "任务生成成功！共计：" + listModel.Count + "条"));
                }
                catch (Exception ex)
                {
                    DebugLogsList.Add(new RunLogs(Model.Id, DateTime.Now, Model.Name + "任务生成失败！"));
                }
            }
        }

        /// <summary>
        /// 递归计算出所有货位
        /// </summary>
        /// <param name="list"></param>
        /// <param name="count"></param>
        /// <param name="result"></param>
        /// <param name="data"></param>
        private static void Descartes(List<string[]> list, int count, List<string> result, string data)
        {
            // 获取当前数组
            string[] curr = list[count];
            foreach (var item in curr)
            {
                if (count + 1 < list.Count)
                {
                    // 跳至下一层
                    Descartes(list, count + 1, result, (data.Length > 0 ? data + "-" : data) + item);
                }
                else
                {
                    // 达到最底层时将拼接的值存入结果列表中
                    result.Add((data.Length > 0 ? data + "-" : data) + item);
                }
            }
        }

        /// <summary>
        /// 生成两个数组，同一位置不相等的
        /// </summary>
        private void GetRandomArray(int iLength, out int[] array1, out int[] array2)
        {
            array1 = Enumerable.Range(0, iLength).OrderBy(x => Guid.NewGuid()).ToArray();
            array2 = Enumerable.Range(0, iLength).OrderBy(x => Guid.NewGuid()).ToArray();

            bool isEquel = false;
            for (int i = 0; i < iLength; i++)
            {
                if (array1[i] == array2[i])
                {
                    array2 = Enumerable.Range(0, iLength).OrderBy(x => Guid.NewGuid()).ToArray();
                    isEquel = true;
                    break;
                }
            }
            if (isEquel)
            {
                GetRandomArray(iLength, out array1, out array2);
            }
        }

        /// <summary>
        /// 对生成好的作业信息排序
        /// </summary>
        /// <param name="list">生成的作业信息集合</param>
        /// <param name="listTemp">排列完的作业信息集合</param>
        /// <param name="Location">第一个任务起始货位</param>
        private void SortList(List<StackerTasksModel> list, List<StackerTasksModel> listTemp, string Location)
        {
            var model = list.Find(a => a.StartLocation == Location);
            if (model != default)
                listTemp.Add(model);
            if (list.Count > listTemp.Count)
            {
                SortList(list, listTemp, model.EndLocation);
            }
        }

        #endregion

        #region 执行作业相关方法
        private async void AutoExecuteTask(TimeSpan timeSpan)
        {
            //执行任务前，判断是否正在执行任务
            if (WindowProperty.isExecute)
                return;

            await Task.Delay(timeSpan);

            //等待时间过后再一次判断是否为自动
            if (!WindowProperty.isAuto)
                return;

            ExecuteTask();
        }
        private async void HalfExecuteTask()
        {
            //执行任务前，判断是否正在执行任务
            if (WindowProperty.isExecute)
                return;

            await Task.Delay(0);

            ExecuteTask();
        }
        private async void HandExecuteTask()
        {
            await Task.Delay(0);
            StackerTasksModel taskModel = new();
            taskModel.StartLocation = WindowProperty.StartRowNum + "-" + WindowProperty.StartColumNum + "-" + WindowProperty.StartLayerNum;
            taskModel.EndLocation = WindowProperty.EndRowNum + "-" + WindowProperty.EndColumNum + "-" + WindowProperty.EndLayerNum;
            taskModel.Id = DataVisitor.AddStackerTasks(taskModel);
            if (taskModel.Id > 0)
            {
                taskModel.StartLocation = Model.LanewayNo + "-" + taskModel.StartLocation;
                taskModel.EndLocation = Model.LanewayNo + "-" + taskModel.EndLocation;
                ExecuteTask(taskModel);
            }
        }

        /// <summary>
        /// 执行作业,自动和半自动
        /// </summary>
        private void ExecuteTask()
        {

            if (ListStackerTask.Where(a => !a.IsExecute).Count() > 0)
            {
                StackerTasksModel taskModel = ListStackerTask.Where(a => !a.IsExecute).OrderBy(a => a.Id).First();
                taskModel.StartLocation = Model.LanewayNo + "-" + taskModel.StartLocation;
                taskModel.EndLocation = Model.LanewayNo + "-" + taskModel.EndLocation;

                ExecuteTask(taskModel);
            }
        }
        /// <summary>
        /// 执行作业，手动
        /// </summary>
        /// <param name="taskModel"></param>
        private void ExecuteTask(StackerTasksModel taskModel)
        {
            //将任务写入PLC中
            if (StackerVisitor.SendTask(taskModel, (logsText) =>
            {
                DebugLogsList.Add(new RunLogs(Model.Id, DateTime.Now, logsText, LogsTypeEnum.故障));
            }))
            {
                WindowProperty.isExecute = true;
                //写入成功
                // ListStackerTask.Where(a => !a.IsExecute).OrderBy(a => a.Id).First().IsExecute = true;
                ListStackerTask.Remove(taskModel);
                DicTaskList["Current"] = taskModel;
                if (ListStackerTask.Count > 0)
                    DicTaskList["Next"] = ListStackerTask.Where(a => !a.IsExecute).OrderBy(a => a.Id).First();
                else
                    DicTaskList["Next"] = null;
                DebugLogsList.Add(new RunLogs(Model.Id, DateTime.Now, "任务【" + taskModel.Id.ToString().PadLeft(4, '0') + "】写入成功，正在执行中！"));

                //写入成功后，修改任务状态，变为已经执行
                taskModel.IsExecute = true;
                taskModel.IsMalfunction = false;
                DataVisitor.EditTask(taskModel);

                //写入成功后，计时器开始计时
                timer.Start();
                WindowProperty.isExecute = true;
            }
            else
            {
                //写入失败
                DebugLogsList.Add(new RunLogs(Model.Id, DateTime.Now, "任务【" + taskModel.Id.ToString().PadLeft(4, '0') + "】写入失败！", LogsTypeEnum.故障));
            }
        }


        /// <summary>
        /// 获取任务完成情况
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void TaskProcss(object sender, EventArgs e)
        {
            if (StackerVisitor.TaskProcss(DicTaskList["Current"].Id))
            {
                //执行完成后，将正在执行状态改为false
                WindowProperty.isExecute = false;
                DebugLogsList.Add(new RunLogs(Model.Id, DateTime.Now, "任务【" + DicTaskList["Current"].Id.ToString().PadLeft(4, '0') + "】执行完成！"));

                //任务执行完成后，停止计时
                timer.Stop();

                DicTaskList["Prev"] = DicTaskList["Current"];
                DicTaskList["Current"] = null;

                if (WindowProperty.isAuto)
                {
                    AutoExecuteTask(TimeSpan.FromSeconds(3));
                }
            }
        }
        #endregion

        #region 运行监控相关方法
        private void Monitor(TimeSpan timeSpan)
        {
            if (!PLC.Connected() || StackerVisitor == null)
            {
                return;
            }

            MonitorTimer.Interval = timeSpan;
            MonitorTimer.Tick += MonitorTimer_Tick;
            MonitorTimer.Start();

            StackerVisitor.ReadPLC((logsText) =>
            {
                DebugLogsList.Add(new RunLogs(Model.Id, DateTime.Now, logsText, LogsTypeEnum.故障));
            });
        }

        private void MonitorTimer_Tick(object sender, EventArgs e)
        {
            StackerVisitor.ReadPLC((logsText) =>
            {
                DebugLogsList.Add(new RunLogs(Model.Id, DateTime.Now, logsText, LogsTypeEnum.故障));
            });
        }
        #endregion
    }

    /// <summary>
    /// 界面元素控件集合
    /// </summary>
    public class RunWindowProperty : ObservableObject
    {
       
        private Brush _txtConnForeground;
        /// <summary>
        /// 连接状态显示字体颜色
        /// </summary>
        public Brush txtConnForeground
        {
            get { return _txtConnForeground; }
            set { _txtConnForeground = value; RaisePropertyChanged(() => txtConnForeground); }
        }


        /// <summary>
        /// 连接状态显示字体颜色
        /// </summary>
        private string _txtConnText;

        public string txtConnText
        {
            get { return _txtConnText; }
            set { _txtConnText = value; RaisePropertyChanged(() => txtConnText); }
        }

        /// <summary>
        /// 连接至PLC按钮
        /// </summary>
        private string _tbtnConn;

        public string tbtnConn
        {
            get { return _tbtnConn; }
            set { _tbtnConn = value; RaisePropertyChanged(() => tbtnConn); }
        }


        /// <summary>
        /// 自动执行点选是否选中
        /// </summary>
        public bool isCheck_Auto { get; set; }
        /// <summary>
        /// 手动执行点选是否选中
        /// </summary>
        public bool isCheck_Hand { get; set; }
        /// <summary>
        /// 半自动执行点选是否选中
        /// </summary>
        public bool isCheck_Half { get; set; }
        /// <summary>
        /// 是否自动执行
        /// </summary>
        public bool isAuto { get; set; }

        private bool _isExecute;
        /// <summary>
        /// 是否正在执行
        /// </summary>
        public bool isExecute
        {
            get { return _isExecute; }
            set { _isExecute = value; RaisePropertyChanged(() => isExecute); }
        }

        private string _txtAutoExecute;
        public string txtAutoExecute
        {
            get { return _txtAutoExecute; }
            set { _txtAutoExecute = value; RaisePropertyChanged(() => txtAutoExecute); }
        }

        private string _startRowNum;
        public string StartRowNum
        {
            get { return _startRowNum; }
            set { _startRowNum = value; RaisePropertyChanged(() => StartRowNum); }
        }

        private string _startColumNum;
        public string StartColumNum
        {
            get { return _startColumNum; }
            set { _startColumNum = value; RaisePropertyChanged(() => StartColumNum); }
        }
        private string _startLayerNum;
        public string StartLayerNum
        {
            get { return _startLayerNum; }
            set { _startLayerNum = value; RaisePropertyChanged(() => StartLayerNum); }
        }

        private string _endRowNum;
        public string EndRowNum
        {
            get { return _endRowNum; }
            set { _endRowNum = value; RaisePropertyChanged(() => EndRowNum); }
        }
        private string _endColumNum;
        public string EndColumNum
        {
            get { return _endColumNum; }
            set { _endColumNum = value; RaisePropertyChanged(() => EndColumNum); }
        }
        private string _endLayerNum;
        public string EndLayerNum
        {
            get { return _endLayerNum; }
            set { _endLayerNum = value; RaisePropertyChanged(() => EndLayerNum); }
        }





        public RunWindowProperty()
        {
            tbtnConn = "连接PLC";
            txtConnForeground = Brushes.Red;
            txtConnText = "未连接";
            isCheck_Auto = true;
            isCheck_Hand = false;
            isCheck_Half = false;

            isAuto = false;
            txtAutoExecute = "自动执行";
            isExecute = false;
        }
    }
}
